/*
 * Copyright (c) 2011, Marc-Andre Ferland - madrang@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the The Warrent Team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *     * This software is free for non-commercial use. You may not use this
 *       software, in whole or in part, in support of any commercial product
 *       without the express consent of the author.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE WARRENT TEAM BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using System.Threading;
using System.Collections.Generic;

namespace OsHelper
{
	public delegate void VoidDelegate();
	public class Worker
	{
		private Queue<VoidDelegate> qu_Delegate;
		private Thread th_Owner;
		private ManualResetEvent mre_WaitInvoke;
		
		public Worker() : this(System.Threading.Thread.CurrentThread) { }
		public Worker(Thread Owner)
		{
			this.mre_WaitInvoke = new ManualResetEvent(false);
			this.th_Owner = Owner;
			this.qu_Delegate = new Queue<VoidDelegate>();
		}
		
		public void Invoke(VoidDelegate MyDelegate)
		{
			if(this.IsOwner) {
				VoidDelegate ToCall = SafeCall(MyDelegate);
				ToCall();
			} else {
				lock(this.qu_Delegate)
					this.qu_Delegate.Enqueue(MyDelegate);
				this.mre_WaitInvoke.Set();
			}
		}
		
		public void DoWork()
		{
			this.DoWork(0);
		}
		
		public void DoWork(int MillisecondTimeout)
		{
			if(!this.mre_WaitInvoke.WaitOne(MillisecondTimeout)) return;
			
			while(this.qu_Delegate.Count > 0) {
				VoidDelegate ToCall;
				lock(this.qu_Delegate)
					ToCall = SafeCall(this.qu_Delegate.Dequeue());
				ToCall();
			}
			this.mre_WaitInvoke.Reset();
		}
		
		private bool IsOwner
		{
			get { return (System.Threading.Thread.CurrentThread == this.th_Owner); }
		}
		
		public WaitHandle WaitHandle {
			get { return this.mre_WaitInvoke; }
		}
		
		#region Static
		
		public static System.Threading.Thread RunOnThread<TSender> (VoidDelegate MyDelegate) {
			return RunOnThread<TSender> (MyDelegate, string.Format("{0}: VoidDelegate", typeof (TSender).Name));
		}
		public static System.Threading.Thread RunOnThread (VoidDelegate MyDelegate)
		{
			return RunOnThread (MyDelegate, "VoidDelegate");
		}
		public static System.Threading.Thread RunOnThread<TSender> (VoidDelegate MyDelegate, string ThreadName)
		{
			return UnsafeRunOnThread(SafeCall<TSender>(MyDelegate), ThreadName);
		}
		public static System.Threading.Thread RunOnThread (VoidDelegate MyDelegate, string ThreadName)
		{
			return UnsafeRunOnThread(SafeCall(MyDelegate), ThreadName);
		}
		
		private static System.Threading.Thread UnsafeRunOnThread (VoidDelegate MyDelegate, string ThreadName)
		{
			Thread newThread = new Thread (() => {
				MyDelegate.Invoke();
			});
			
			newThread.IsBackground = true;
			newThread.Name = ThreadName;
			newThread.Start ();
			return newThread;
		}
		
		public static WaitHandle RunOnPool<TSender> (VoidDelegate MyDelegate)
		{
			return UnsafeRunOnPool(SafeCall<TSender>(MyDelegate));
		}
		public static WaitHandle RunOnPool(VoidDelegate MyDelegate)
		{
			return UnsafeRunOnPool(SafeCall(MyDelegate));
		}
		
		private static WaitHandle UnsafeRunOnPool(VoidDelegate MyDelegate)
		{
			VoidDelegate ToInvoke = MyDelegate;
			ManualResetEvent ManualLock = new ManualResetEvent(false);
			
			AsyncCallback CallBack = delegate(IAsyncResult ar) {
				ToInvoke.EndInvoke(ar);
				ManualLock.Set();
			};
			
			ToInvoke.BeginInvoke(CallBack, null);
			
			return ManualLock;
		}
		
		public static VoidDelegate Delay (VoidDelegate MyDelegate, TimeSpan MyDelay)
		{
			return delegate {
				System.Threading.Thread.Sleep (MyDelay);
				MyDelegate ();
			};
		}
		
		public static VoidDelegate Delay (VoidDelegate MyDelegate, int MyDelay)
		{
			return Delay (MyDelegate, new TimeSpan (0, 0, 0, 0, MyDelay));
		}
		
		private static VoidDelegate SafeCall<TSender> (VoidDelegate MyDelegate)
		{
			return delegate {
				try {
					MyDelegate ();
				} catch (ThreadAbortException e){
					DebugTools.Logger.Default.Debug<TSender> ("{0}: {1}", Thread.CurrentThread.Name, e.Message);
				} catch (Exception e) {
					DebugTools.Logger.Default.Error<TSender> ("{0}: {1}", Thread.CurrentThread.Name, e.Message);
					DebugTools.Logger.Default.Debug<TSender> (e.StackTrace);
				}
			};
		}
		private static VoidDelegate SafeCall(VoidDelegate MyDelegate)
		{
			return delegate {
				try {
					MyDelegate ();
				} catch (ThreadAbortException e){
					DebugTools.Logger.Default.Debug ("{0}: {1}", Thread.CurrentThread.Name, e.Message);
				} catch (Exception e) {
					DebugTools.Logger.Default.Error ("{0}: {1}", Thread.CurrentThread.Name, e.Message);
					DebugTools.Logger.Default.Debug (e.StackTrace);
				}
			};
		}
		
		#endregion
		
	}
}
